TCP发送窗口、接收窗口、wmem、rmem 关系

基础环境

服务器信息

Client:192.168.56.100

Server:192.168.56.101

OS:CentOS Linux release 7.9.2009 (Core) 3.10.0-1160.el7.x86_64

大致思路

wmem 对应 Send Buffer,从 TCP 的概念可以理解为 发送窗口,字母 w 可以理解成 write

rmem 对应 Receive Buffer,从 TCP 的概念可以理解为 接收窗口

实验过程是模拟一个下载过程,也就是大致过程是 server 发送文件,所以调整 wmwmclient 接收文件,所以调整 rmen

目的如下:

写死 Linux参数让传输速度慢下来
改接收端:sysctl -w "net.ipv4.tcp_rmem=4096  4096    4096"
或者
改发送端:sysctl -w "net.ipv4.tcp_wmem=4096  4096    4096" 

继续调整rtt、丢包率,curl限制速度等,抓包分析看到的所有现象

系统默认参数如下

$ sudo sysctl -a | egrep "rmem|wmem|tcp_mem|adv_win|moderate"
net.core.rmem_default = 212992
net.core.rmem_max = 212992
net.core.wmem_default = 212992
net.core.wmem_max = 212992
net.ipv4.tcp_adv_win_scale = 1
net.ipv4.tcp_mem = 42459    56612   84918
net.ipv4.tcp_moderate_rcvbuf = 1
net.ipv4.tcp_rmem = 4096    87380   6291456 //最小值  默认值  最大值
net.ipv4.tcp_wmem = 4096    16384   4194304 //最小值  默认值  最大值
net.ipv4.udp_rmem_min = 4096
net.ipv4.udp_wmem_min = 4096
vm.lowmem_reserve_ratio = 256   256 32

开始命令

# Client && Server
clientIP="192.168.56.100"
serverIP="192.168.56.101"

# Server
# 启动命令的地方存在一个 2个多G 的文件
python -m SimpleHTTPServer 8089

## Client
curl http://192.168.56.101:8089/iso.tar --output ./result

# 测试宽带
iperf3 -s
iperf3 -c ${serverIP} -t 60 -i 1
# 无额外设置情况下宽带结果
大概是 2.69 Gbits/sec = 2860 Mbps

实验结果信息汇总

--正常 127 MB/s

--延时 100ms,5.48 MB/s
client 角度看的话,server发的包大小不稳定,60000-10000都有
server 是以比较稳定的处理包,然后有100ms的延时

--延时 100ms 且client  rmem 改成 4096,17 KB/s
client 角度来看,包比较乱
server 角度来说,连续小包(几千的样子)汇总发送,包的大小会有 重复递增的样子

--延时 100ms 且server  wmem 改成 4096,80 KB/s
client 角度来看,一个ACK,然后一个稳定的接收 8258 length 的包
server 是以比较稳定的处理包,然后有100ms的延时

--延时 10ms 且client  rmem 改成 4096,17 KB/s
client 角度来看,包比较乱
server 角度来说,连续小包(几千的样子)汇总发送,包的大小会有 重复递增的样子

实验记录

实验一:正常状态

# Client
tcpdump -i enp0s8 -n host  ${serverIP} -w client_01.pcap
# Server
tcpdump -i enp0s8 -n host  ${clientIP} -w server_01.pcap

# 下载结果 大概平均下载速度是 127 MB/s
$ curl http://192.168.56.101:8089/iso.tar --output ./result_01
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 2539M  100 2539M    0     0   127M      0  0:00:19  0:00:19 --:--:--  130M

可以看到截图里,时间都是以 0.000X 秒递增的

client_01 截图

server_01 截图

TCP Windows Scale

Window Scale 选项只能与 TCP SYN、SYN/ACK 一起使用。TCP 早期的时候带宽很小,所以最大接收窗口被定义成 65535 字节( TCP Header 的 Windows 最大为 16 bit )

由于不够用,所以通过 TCP options 定义了 Windows Scale,其声明了一个 Shift count,作为2的指数,再乘以 TCP 头中定义的接收窗口,就得到真正的 TCP 接收窗口了。

下图为 SYN/ACK 中可以看到 Windows Scale 为 7

image-20230420161433041

下图为真实的 Windows size:29312 = 229 * 128

image-20230420161615849

Window size

TCP 层的 Windows size (或者如上述截图的 Windows 、Calculated windows size),是在向对方声明自己的接收窗口,而不是发送窗口

发送窗口决定了一口气能发多少字节,而 MSS 决定了这些字节要分多少个包发完。至于为什么 MSS=1460 的情况下,但是抓包的结果仍然有大于他的,这就是另外一个故事了,关键字:TCP Segment Offload

实验二:server 增加 100ms 延时

# Server
tc qdisc show dev enp0s8
tc qdisc del dev enp0s8 root
tc qdisc add dev enp0s8 root netem delay 100ms
# Client
tcpdump -i enp0s8 -n host  ${serverIP} -w client_delay_100ms.pcap
# Server
tcpdump -i enp0s8 -n host  ${clientIP} -w server_delay_100ms.pcap

# 结果,大概平均下载速度是 5.48 MB/s
$ curl http://192.168.56.101:8089/iso.tar --output ./result
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 2539M  100 2539M    0     0  5619k      0  0:07:42  0:07:42 --:--:-- 4563k

server 抓包详情

image-20230501160506654

Wireshark 菜单路径:Statistics >> TCP Stream Graphs >> Time Sequence (Stevens)

Time Sequence (Stevens)图能看到两个点之间,time相减为 0.1秒

我的实验过程中,Round Trip Time Graphs 看不出来 rtt 是固定 100ms 的。但是可以通过点击 Switch Direction 来看出 rtt

image-20230501160602666

实验三:server 增加 100ms 延时,client 修改 rmem

# 两边原始值
sysctl -w net.ipv4.tcp_wmem="4096 16384 4194304"
sysctl -w net.ipv4.tcp_rmem="4096 87380 6291456"
# server
tc qdisc show dev enp0s8
tc qdisc del dev enp0s8 root
tc qdisc add dev enp0s8 root netem delay 100ms
# client
sysctl -w net.ipv4.tcp_rmem="4096 4096 4096"

# Client
tcpdump -i enp0s8 -n host  ${serverIP} -w client_delayServer_100ms_rmem_Client_4096.pcap
# Server
tcpdump -i enp0s8 -n host  ${clientIP} -w server_delayServer_100ms_rmem_Client_4096.pcap

# 结果, 大概平均下载速度是 17 KB/s
$ curl http://192.168.56.101:8089/iso.tar --output ./result_01
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0 2539M    0 1399k    0     0  14436      0 51:14:22  0:01:39 51:12:43 14458

截图见实验一结论

实验四:server 增加 100ms 延时,server 修改wmem

# 两边原始值
sysctl -w net.ipv4.tcp_wmem="4096 16384 4194304"
sysctl -w net.ipv4.tcp_rmem="4096 87380 6291456"
# Server
tc qdisc show dev enp0s8
tc qdisc del dev enp0s8 root
tc qdisc add dev enp0s8 root netem delay 100ms
sysctl -w net.ipv4.tcp_wmem="4096 4096 4096"
# Client
tcpdump -i enp0s8 -n host  ${serverIP} -w client_delayServer_100ms_wmem_Server_4096.pcap
# Server
tcpdump -i enp0s8 -n host  ${clientIP} -w server_delayServer_100ms_wmem_Server_4096.pcap

# 结果, 大概平均下载速度是 80 KB/s
$ curl http://192.168.56.101:8089/iso.tar --output ./result_03
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0 2539M    0 13.3M    0     0  81082      0  9:07:22  0:02:52  9:04:30 81157

image-20230426200129823

Window Scaling 截图,绿线后面稳定的时候,值是 182272,和抓包截图里 Client 的 win=182272 也对应的上,win 是 client 向对方声明自己的接收窗口。从下载网速来看也就是虽然 client 声明了自己的接收窗口很大,但是 server 端的 wmem 比较小,所以网速也不快。

image-20230426193723153

一个笔直的蓝色线 对应的从 No 180-182,对应的大小为:2896、5792、8192,抓包见下面截图,和抓包详情截图的 Len 字段也对得上。

wireshark 的 Length 列 减去 66 ,就等于包详情里的 Len,至于为什么是 66 不太清楚,但是可以从 ACK 响应是 66 辅助确认。

image-20230426195023142

实验五:server 增加 10ms 延时,client 修改 rmem

# 两边原始值
sysctl -w net.ipv4.tcp_wmem="4096 16384 4194304"
sysctl -w net.ipv4.tcp_rmem="4096 87380 6291456"
# Server
tc qdisc show dev enp0s8
tc qdisc del dev enp0s8 root
tc qdisc add dev enp0s8 root netem delay 10ms
# client
sysctl -w net.ipv4.tcp_rmem="4096 4096 4096"

# Client
tcpdump -i enp0s8 -n host  ${serverIP} -w client_delayServer_10ms_rmem_Client_4096.pcap
# Server
tcpdump -i enp0s8 -n host  ${clientIP} -w server_delayServer_10ms_rmem_Client_4096.pcap

# 结果, 大概平均下载速度是 123 KB/s
$ curl http://192.168.56.101:8089/iso.tar --output ./result_01
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0 2539M    0 20.6M    0     0   123k      0  5:50:08  0:02:50  5:47:18  123k

结论二 截图

实验六:curl 增加速度控制

# client & server 保持默认值不动,限速设置小一点,抓包分析更好一点,分别尝试了1k、100k、1M
curl --limit-rate 1k http://192.168.56.101:8089/iso.tar --output ./result_04

# Client
tcpdump -i enp0s8 -n host  ${serverIP} -w client_curl_1k.pcap
# Server
tcpdump -i enp0s8 -n host  ${clientIP} -w server_curl_1k.pcap

TCP Window Full 意味着窗口填满,可以看下这篇文章,说的很详细:Analyse Slow Networks with TCP Zero Window

image-20230427171447952

结论

结论一:改小 client rmem 和 server wmem 来对比对速度的影响

实验三 和 实验四:平均下载速度为:17 KB/s 和 80 KB/s 的对比

相比而言,server 改小了 wmen 速度还凑合,因为server 每次收到ack,立即释放 wmem 来发新的网络包 (内存级别的时延);

如果 client 的 rmem 比较小,当 rmem 满了到应用读走 rmem,rmem 有空闲后需要 rtt 时间反馈到 server 端,server 端才会继续发包。(网络级时延比内存级时延高几个数量级)

一句话总结:就是 rmem 从有空到包进来会有很大的间隔 (rtt) , wmem 有空到写包进来没有时延。

plantegg 任总的这张图反复看,能有点理解了

时间比较

分析 实验三和实验四中 server 端抓包的结果:

实验三 rmen 的截图,从 No 15 开始看,差不多是 server > client,【TCP Window Full 过会看】然后 No 17 中,client > server ACK 回复,然后过了 0.1 秒,No 18 又开始发送给 client 了。

而且 TCP Window Full 很频繁,窗口被填满了,所以下载的速度很慢。截图里能看到 client 的 win=1460。

简单按照步骤描述下过程

1、No 11: client 表示我的接收窗口为 1460

2、No 12: server 表示我的接收窗口为 29056,以及发送了 730 长度的包

3、No 13: server 继续发送了 730 长度的包,由于 730 + 730 等于 client 的接收窗口 1460,所以提示 TCP WIndow Full

4、No 14: client 继续说我的接收窗口为 1460

实验四 wmen 的截图,server 几个包发给 client,然后 client ack 回复,反复该过程,包的发送比较平稳

结论二:rmem 改小,查看 调整 rrt 后来对比速度

实验三 和 实验五 都是修改 client rmen = 4096,只是一个是 server 端设置了 100ms 和 10ms 延时,但是结果是 17 KB/s 和 123 KB/s 的差距。

rrt 增大了,下载速度会下降。下面两个截图,10ms 的 window Scaling 和 Throughput 都要比 100ms 的密集

plantegg 任总的这张图反复看【结论二的只需要看图片左边即可】,能有点理解了

延时增加了,那说明链路上的包也多了,client 接收包的速度慢了,那自然下载速度变慢。

rmem_rtt

结论三:汇总:网速和 wmem rmem rtt 带宽 的关系

减少 rtt 和 增大带宽 影响带宽就不说了。

client 的 recv buffer 太小 且 rtt 很大的情况下,网速会变慢。

增加 server 的 send buffer (对应 wmem),相当于仓库发货比较强

增加 client 的 recv buffer (对应 rmem),相当于仓库收获比较强

ss 计算 tcp buffer size

man ss 链接

# 注意下,tb 表示 snd_buf,字母 t 可以理解为 Transmit
       -m, --memory
              Show socket memory usage. The output format is:

              skmem:(r<rmem_alloc>,rb<rcv_buf>,t<wmem_alloc>,tb<snd_buf>,
                            f<fwd_alloc>,w<wmem_queued>,o<opt_mem>,
                            bl<back_log>,d<sock_drop>)

待办

BDP

丢失率

参考链接

Analyse Slow Networks with TCP Zero Window

TCP性能和发送接收窗口、Buffer的关系

长肥管道(LFT)中TCP的艰难处境与打法

Why Your Application only Uses 10Mbps Even the Link is 1Gbps?

TCP Throughput Calculator